﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

using LostLib;
using LostLib.Net;
using System.Threading;
using System.Windows.Forms;

namespace lsocks
{
	class lsocks
	{
		static IPAddress localIP;
		static int localPort;
		static bool noGUI;

		static void Parse(List<string> args)
		{
			IEnumerator<string> enumerator = args.GetEnumerator();
			while (enumerator.MoveNext())
			{
				switch (enumerator.Current)
				{
				case "-ip":
					if (localIP != null) throw new ArgumentException("duplicate -ip");
					localIP = IPAddress.Parse(enumerator.GetNext());
					break;
				case "-port":
					if (localPort != 0) throw new ArgumentException("duplicate -port");
					localPort = int.Parse(enumerator.GetNext());
					break;
				case "-nogui":
					noGUI = true;
					break;
				default:
					throw new ArgumentException("Unknown argument: " + enumerator.Current);
				}
			}

			if (localIP == null)
			{
				var entry = Dns.GetHostEntry("localhost");
				localIP = entry.AddressList.First();
			}
			if (localPort == 0) localPort = 3218;
		}

		static void Main(string[] args)
		{
			Parse(new List<string>(args));

			var listener = new TcpListener(localIP, localPort);

			listener.Start();
			if (noGUI)
				Console.WriteLine("running on {0}:{1}...", localIP, localPort);
			else
				MessageBox.Show(string.Format("running on {0}:{1}...", localIP, localPort));

			while (true)
			{
				var tcpclient = listener.AcceptTcpClient();
				try
				{
					var client = SocksServerClient.Start(tcpclient);
					client.TransferError += new EventHandler<ExceptionHandlerEventArgs>(client_TransferError);

					Console.WriteLine("{0} connected", tcpclient.Client.RemoteEndPoint);

					ThreadPool.QueueUserWorkItem(new WaitCallback(AcceptClient), client);
				} catch { }
				
			}
		}

		static void client_TransferError(object sender, ExceptionHandlerEventArgs e)
		{
			var transferError = e.Exception as NetworkTransferException;
			if (transferError != null)
			{
				Console.WriteLine("{0} {1}", transferError.ServerToClient ? "-->" : "<--", transferError.Message);
				e.Reaction = ExceptionReaction.Abort;
				return;
			}
			Console.WriteLine("{0}: {1}", e.Exception.GetType(), e.Exception.Message);
		}

		static void AcceptClient(object obj)
		{
			var client = obj as SocksServerClient;
			client.Run();
		}
	}
}
